﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace UtilZ.Dotnet.Ex.Model
{
#pragma warning disable 8500

    /// <summary>
    /// 通用类型数据片
    /// </summary>
    unsafe public class DataSpan<T>
    {
        private DataSpanDataType _dataType;
        /// <summary>
        /// 数据片中的数据类型
        /// </summary>
        public DataSpanDataType DataType
        {
            get { return this._dataType; }
        }

        private T[] _array = null;
        /// <summary>
        /// 数组数据
        /// </summary>
        public T[] Array
        {
            get
            {
                return this._array;
            }
        }

        private IList<T> _items = null;
        /// <summary>
        /// 集合数据
        /// </summary>
        public IList<T> Items
        {
            get
            {
                return this._items;
            }
        }


        private T* _originDataPtr = null;
        /// <summary>
        /// 原始数据指针
        /// </summary>
        public T* OriginDataPtr
        {
            get { return this._originDataPtr; }
        }

        private T* _dataPtr = null;
        /// <summary>
        /// 数据指针
        /// </summary>
        public T* DataPtr
        {
            get
            {
                return this._dataPtr;
            }
        }


        private int? _offset = 0;
        /// <summary>
        /// 相对于数组或原始数据指针数据起始偏移位置
        /// </summary>
        public int Offset
        {
            get
            {
                if (this._offset.HasValue)
                {
                    return this._offset.Value;
                }
                else
                {
                    if (this._longOffset > 0L)
                    {
                        throw new InvalidOperationException($"当前数据长度超出int.MaxValue值上限,请使用LongOffset获取数据起始偏移位置");
                    }
                    else
                    {
                        return 0;
                    }
                }
            }
        }

        private long _longOffset = 0;
        /// <summary>
        /// 相对于数组或原始数据指针数据起始偏移位置
        /// </summary>
        public long LongOffset
        {
            get
            {
                return this._longOffset;
            }
        }



        private int? _length = 0;
        /// <summary>
        /// 相对于数组或原始数据指针数据起始偏移位置
        /// </summary>
        public int Length
        {
            get
            {
                if (this._length.HasValue)
                {
                    return this._length.Value;
                }
                else
                {
                    if (this._longLength > 0L)
                    {
                        throw new InvalidOperationException($"当前数据长度超出int.MaxValue值上限,请使用LongLength获取数据长度");
                    }
                    else
                    {
                        return 0;
                    }
                }
            }
        }

        private long _longLength = 0L;
        /// <summary>
        /// 数据长度
        /// </summary>
        public long LongLength
        {
            get
            {
                return this._longLength;
            }
        }



        /// <summary>
        /// 数据容量
        /// </summary>
        private int? _capcity = 0;
        /// <summary>
        /// 数据容量
        /// </summary>
        public int Capcity
        {
            get
            {
                if (this._capcity.HasValue)
                {
                    return this._capcity.Value;
                }
                else
                {
                    if (this._longCapcity > 0L)
                    {
                        throw new InvalidOperationException($"当前数据长度超出int.MaxValue值上限,请使用LongCapcity获取数据容量");
                    }
                    else
                    {
                        return 0;
                    }
                };
            }
        }

        /// <summary>
        /// 数据容量
        /// </summary>
        private long _longCapcity = 0L;
        /// <summary>
        /// 数据容量
        /// </summary>
        public long LongCapcity
        {
            get
            {
                return this._longCapcity;
            }
        }


        /// <summary>
        /// 获取或设置用户定义的数据
        /// </summary>
        public object Tag { get; set; }


        /// <summary>
        /// 获取或设置值
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        /// <exception cref="IndexOutOfRangeException">索引越界异常</exception>
        public T this[int index]
        {
            get
            {
                if (index < 0 || index >= this._length)
                {
                    throw new IndexOutOfRangeException($"索引越界,index:{index},当前数据长度:{this._length}");
                }

                switch (this._dataType)
                {
                    case DataSpanDataType.Array:
                        return this._array[this._offset.Value + index];
                    case DataSpanDataType.Collection:
                        return this._items[this._offset.Value + index];
                    case DataSpanDataType.DataPtr:
                        return this._dataPtr[index];
                    default:
                        throw new NotImplementedException($"未实现索引的数据类型{this._dataType.ToString()}");
                }
            }
            set
            {
                if (index < 0 || index >= this._length)
                {
                    throw new IndexOutOfRangeException($"索引越界,index:{index},当前数据长度:{this._length}");
                }

                switch (this._dataType)
                {
                    case DataSpanDataType.Array:
                        this._array[this._offset.Value + index] = value;
                        break;
                    case DataSpanDataType.Collection:
                        this._items[this._offset.Value + index] = value;
                        break;
                    case DataSpanDataType.DataPtr:
                        this._dataPtr[index] = value;
                        break;
                    default:
                        throw new NotImplementedException($"未实现索引的数据类型{this._dataType.ToString()}");
                }
            }
        }



        /// <summary>
        /// 构造函数
        /// </summary>
        public DataSpan()
        {

        }

        /// <summary>
        /// 构造函数-数组数据片
        /// </summary>
        /// <param name="buffer">内存数组buffer数据</param>
        /// <param name="startIndex">数据起始偏移位置,小于等于0从头开始</param>
        /// <param name="length">数据长度，小于0到结尾</param>
        public DataSpan(T[] buffer, int startIndex = -1, int length = -1)
            : this()
        {
            this.Update(buffer, startIndex, length);
        }

        /// <summary>
        /// 构造函数-集合数据片
        /// </summary>
        /// <param name="items">内存数组buffer数据</param>
        /// <param name="startIndex">数据起始偏移位置,小于等于0从头开始</param>
        /// <param name="length">数据长度，小于0到结尾</param>
        public DataSpan(IList<T> items, int startIndex = -1, int length = -1)
            : this()
        {
            this.Update(items, startIndex, length);
        }

        /// <summary>
        /// 构造函数-集合数据片
        /// </summary>
        /// <param name="dataPtr">数据指针</param>
        /// <param name="length">数据长度</param>
        /// <param name="capcity">指针容量</param>
        public DataSpan(T* dataPtr, int length, int capcity)
            : this()
        {
            this.Update(dataPtr, length, capcity);
        }

        /// <summary>
        /// 构造函数-集合数据片
        /// </summary>
        /// <param name="dataPtr">数据指针</param>
        /// <param name="length">数据长度</param>
        /// <param name="capcity">指针容量</param>
        public DataSpan(T* dataPtr, long length, long capcity)
            : this()
        {
            this.Update(dataPtr, length, capcity);
        }



        /// <summary>
        /// 更新数据片
        /// </summary>
        /// <param name="startIndex">数据起始偏移位置,小于等于0从头开始</param>
        /// <param name="length">数据长度，小于0到结尾</param>
        public void Update(int startIndex, int length)
        {
            if (startIndex < 0 || startIndex >= this._capcity)
            {
                throw new ArgumentOutOfRangeException($"数据起始偏移位置不能小于0且不能大于等于容量值,startIndex无效");
            }

            if (length < 0 || length > this._longCapcity)
            {
                throw new ArgumentOutOfRangeException($"长度值不能小于0且不能超出容量上限,length无效,");
            }

            if (startIndex + length > this._longCapcity)
            {
                throw new ArgumentOutOfRangeException($"数据长度超出容量值.");
            }

            this._offset = 0;
            this._longOffset = 0;

            this._length = length;
            this._longLength = length;

            if (this._dataType == DataSpanDataType.DataPtr)
            {
                this._dataPtr = this._originDataPtr + startIndex;
            }
        }

        /// <summary>
        /// 更新数据片
        /// </summary>
        /// <param name="startIndex">数据起始偏移位置</param>
        /// <param name="length">数据长度</param>
        public void Update(long startIndex, long length)
        {
            if (startIndex < 0 || startIndex >= this._longCapcity)
            {
                throw new ArgumentOutOfRangeException($"数据起始偏移位置不能小于0且不能大于等于容量值,startIndex无效");
            }

            if (length < 0 || length > this._longCapcity)
            {
                throw new ArgumentOutOfRangeException($"长度值不能小于0且不能超出容量上限,length无效,");
            }

            if (startIndex + length > this._longCapcity)
            {
                throw new ArgumentOutOfRangeException($"数据长度超出容量值.");
            }


            if (startIndex > int.MaxValue)
            {
                this._offset = null;
            }
            else
            {
                this._offset = (int)startIndex;
            }
            this._longOffset = startIndex;

            if (length > int.MaxValue)
            {
                this._length = null;
            }
            else
            {
                this._length = (int)length;
            }
            this._longLength = length;

            if (this._dataType == DataSpanDataType.DataPtr)
            {
                this._dataPtr = this._originDataPtr + startIndex;
            }
        }

        /// <summary>
        /// 更新数据片
        /// </summary>
        /// <param name="buffer">内存数组buffer数据</param>
        /// <param name="startIndex">数据起始偏移位置,小于等于0从头开始</param>
        /// <param name="length">数据长度，小于0到结尾</param>
        public void Update(T[] buffer, int startIndex = -1, int length = -1)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            if (startIndex < 0)
            {
                startIndex = 0;
            }
            else if (startIndex >= buffer.Length)
            {
                throw new ArgumentOutOfRangeException($"数据起始偏移位置\"{startIndex}\"无效,超出范围");
            }

            if (length < 0)
            {
                length = buffer.Length - startIndex;
            }

            if (length < 0)
            {
                throw new ArgumentOutOfRangeException($"长度值\"{length}\"无效,超出范围,不能小于0");
            }

            this._array = buffer;

            this._offset = startIndex;
            this._longOffset = startIndex;

            this._length = length;
            this._longLength = length;

            this._capcity = buffer.Length;
            this._longCapcity = buffer.LongLength;

            this._dataType = DataSpanDataType.Array;
        }

        /// <summary>
        /// 更新数据片
        /// </summary>
        /// <param name="items">内存数组buffer数据</param>
        /// <param name="startIndex">数据起始偏移位置,小于等于0从头开始</param>
        /// <param name="length">数据长度，小于0到结尾</param>
        public void Update(IList<T> items, int startIndex = -1, int length = -1)
        {
            if (items == null)
            {
                throw new ArgumentNullException(nameof(items));
            }

            int maxLength = items.Count();
            if (startIndex < 0)
            {
                startIndex = 0;
            }
            else if (startIndex >= maxLength)
            {
                throw new ArgumentOutOfRangeException($"数据起始偏移位置\"{startIndex}\"无效,超出范围");
            }

            if (length < 0)
            {
                length = maxLength - startIndex;
            }

            if (length < 0)
            {
                throw new ArgumentOutOfRangeException($"长度值\"{length}\"无效,超出范围,不能小于0");
            }

            this._items = items;

            this._offset = startIndex;
            this._longOffset = startIndex;

            this._length = length;
            this._longLength = length;

            this._capcity = maxLength;
            this._longCapcity = maxLength;

            this._dataType = DataSpanDataType.Collection;
        }

        /// <summary>
        /// 更新数据片
        /// </summary>
        /// <param name="dataPtr">数据指针</param>
        /// <param name="length">数据长度</param>
        /// <param name="capcity">指针容量</param>
        public void Update(T* dataPtr, int length, int capcity)
        {
            if (length < 0)
            {
                throw new ArgumentOutOfRangeException($"长度值\"{length}\"无效,超出范围,不能小于0");
            }

            if (capcity < length)
            {
                throw new ArgumentOutOfRangeException($"指针容量不能小于数据长度值\"{length}\"");
            }

            this._dataPtr = dataPtr;
            this._originDataPtr = dataPtr;

            this._offset = 0;
            this._longOffset = 0L;

            this._length = length;
            this._longLength = length;

            this._capcity = capcity;
            this._longCapcity = capcity;

            this._dataType = DataSpanDataType.DataPtr;
        }

        /// <summary>
        /// 更新数据片
        /// </summary>
        /// <param name="dataPtr">数据指针</param>
        /// <param name="length">数据长度</param>
        /// <param name="capcity">指针容量</param>
        public void Update(T* dataPtr, long length, long capcity)
        {
            if (length < 0)
            {
                throw new ArgumentOutOfRangeException($"长度值\"{length}\"无效,超出范围,不能小于0");
            }

            if (capcity < length)
            {
                throw new ArgumentOutOfRangeException($"指针容量不能小于数据长度值\"{length}\"");
            }

            this._dataPtr = dataPtr;
            this._originDataPtr = dataPtr;

            this._offset = 0;
            this._longOffset = 0L;

            if (length > int.MaxValue)
            {
                this._length = null;
            }
            else
            {
                this._length = (int)length;
            }
            this._longLength = length;

            if (capcity > int.MaxValue)
            {
                this._capcity = null;
            }
            else
            {
                this._capcity = (int)capcity;
            }
            this._longCapcity = capcity;

            this._dataType = DataSpanDataType.DataPtr;
        }



        /// <summary>
        /// 获取第一项
        /// </summary>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public T First()
        {
            int index = this._offset.Value;
            switch (this._dataType)
            {
                case DataSpanDataType.Array:
                    return this._array[index];
                case DataSpanDataType.Collection:
                    return this._items.ElementAt(index);
                case DataSpanDataType.DataPtr:
                    return this._dataPtr[index];
                default:
                    throw new NotImplementedException();
            }
        }

        /// <summary>
        /// 获取最后一项
        /// </summary>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public T Last()
        {
            int index = this._offset.Value + this._length.Value - 1;
            switch (this._dataType)
            {
                case DataSpanDataType.Array:
                    return this._array[index];
                case DataSpanDataType.Collection:
                    return this._items.ElementAt(index);
                case DataSpanDataType.DataPtr:
                    return this._dataPtr[index];
                default:
                    throw new NotImplementedException();
            }
        }





        /// <summary>
        /// 克隆当前对象,数据重新复制一份
        /// </summary>
        /// <returns>克隆完的数据</returns>
        public DataSpan<T> ToDataSpan()
        {
            T[] buffer;
            switch (this._dataType)
            {
                case DataSpanDataType.Array:
                    if (this._longLength == 0)
                    {
                        return new DataSpan<T>(this._array, 0, 0);
                    }

                    buffer = new T[this._longLength];
                    System.Array.Copy(this._array, this._longOffset, buffer, 0, buffer.Length);
                    return new DataSpan<T>(buffer);
                case DataSpanDataType.Collection:
                    if (this._longLength == 0)
                    {
                        return new DataSpan<T>(this._items, 0, 0);
                    }

                    buffer = new T[this._longLength];
                    int index = this._offset.Value;
                    for (int i = 0; i < buffer.Length; i++)
                    {
                        buffer[i] = this._items.ElementAt(index++);
                    }
                    return new DataSpan<T>(buffer);
                case DataSpanDataType.DataPtr:
                    return new DataSpan<T>(this._dataPtr, this._longLength, this._longLength);
                default:
                    throw new NotImplementedException();
            }
        }

        /// <summary>
        /// 克隆当前对象,数据重新复制一份
        /// </summary>
        /// <returns>克隆完的数据</returns>
        public T[] ToArray()
        {
            if (this._longLength == 0)
            {
#if NET4_0
                return Base.ArrayEx.Empty<T>();
#else
                return System.Array.Empty<T>();
#endif
            }

            if (this._longLength > int.MaxValue)
            {
                throw new InvalidOperationException($"当前数据长度超出int.MaxValue值上限,请使用GetSpanZ替换");
            }

            T[] array = new T[this._length.Value];
            switch (this._dataType)
            {
                case DataSpanDataType.Array:
                    System.Array.Copy(this._array, this._offset.Value, array, 0, array.Length);
                    break;
                case DataSpanDataType.Collection:
                    for (int i = 0; i < array.Length; i++)
                    {
                        array[i] = this._items.ElementAt(i + this._offset.Value);
                    }
                    break;
                case DataSpanDataType.DataPtr:
                    fixed (T* ptr = array)
                    {
                        var byteCount = array.Length * sizeof(T);
                        NativeMemory.Copy(ptr, this._dataPtr, (nuint)byteCount);
                    }
                    break;
                default:
                    throw new NotImplementedException();
            }

            return array;
        }

#if !NET4_0
        /// <summary>
        /// 获取Span数据
        /// </summary>
        /// <returns>Span数据</returns>
        public Span<T> GetSpan()
        {
            if (this._longLength > int.MaxValue)
            {
                throw new InvalidOperationException($"当前数据长度超出int.MaxValue值上限,请使用GetSpanZ替换");
            }

            switch (this._dataType)
            {
                case DataSpanDataType.Array:
                    return new Span<T>(this._array, this._offset.Value, this._length.Value);
                case DataSpanDataType.DataPtr:
                    return new Span<T>(this._dataPtr, this._length.Value);
                default:
                    throw new InvalidOperationException();
            }
        }
#endif


    }



    /// <summary>
    /// 
    /// </summary>
    unsafe public static class DataSpanEx
    {
        /// <summary>
        /// 将DataSpan中的数据写入到BinaryWriter中
        /// </summary>
        /// <param name="writer">BinaryWriter</param>
        /// <param name="data">要写入的数据</param>
        /// <exception cref="NotSupportedException"></exception>
        public static void Write(this BinaryWriter writer, DataSpan<byte> data)
        {
            if (data == null || data.Length <= 0)
            {
                return;
            }

            switch (data.DataType)
            {
                case DataSpanDataType.Array:
                    writer.Write(data.Array, data.Offset, data.Length);
                    break;
                case DataSpanDataType.DataPtr:
                    writer.Write(new Span<byte>(data.DataPtr, data.Length));
                    break;
                case DataSpanDataType.Collection:
                    for (int i = data.Offset; i < data.Length; i++)
                    {
                        writer.Write(data.Items[i]);
                    }
                    break;
                default:
                    throw new NotSupportedException($"不支持有类型数据:{data.DataType.ToString()}");
            }
        }

        /// <summary>
        /// 将数据从BinaryReader读取到DataSpan对象中
        /// </summary>
        /// <param name="reader">BinaryReader</param>
        /// <param name="data">要读取的数据</param>
        /// <param name="length">要读取的数据长度</param>
        public static void Read(this BinaryReader reader, DataSpan<byte> data, int length)
        {
            if (data == null || data.Length <= 0)
            {
                return;
            }

            if (data.OriginDataPtr != null && data.Capcity >= length)
            {
                reader.Read(new Span<byte>(data.OriginDataPtr, length));
                data.Update(data.OriginDataPtr, length, data.LongCapcity);
            }
            else
            {
                if (data.Array == null || data.Array.Length < length)
                {
                    byte[] buffer = reader.ReadBytes(length);
                    data.Update(buffer);
                }
                else
                {
                    reader.Read(data.Array, 0, length);
                    data.Update(0, length);
                }
            }
        }
    }

    /// <summary>
    /// 数据片中的数据类型
    /// </summary>
    public enum DataSpanDataType
    {
        /// <summary>
        /// 数组
        /// </summary>
        Array = 1,

        /// <summary>
        /// 泛型集合
        /// </summary>
        Collection = 2,

        /// <summary>
        /// 数据指针
        /// </summary>
        DataPtr = 3
    }

}
